<?php


namespace Gaoop\Bundle\ApiBundle\EventListener;


use Doctrine\Common\Annotations\AnnotationReader;
use Gaoop\Bundle\ApiBundle\Annotation\ApiParam;
use Gaoop\Bundle\ApiBundle\Exception\ApiParamValidationException;
use Gaoop\Bundle\ApiBundle\Validation\ApiParamValidation;
use Symfony\Component\HttpKernel\Event\ControllerEvent;

class ApiParamListener {

    private $request;
    private $method;
    private $annotationReader;
    private $apiParamValidation;

    public function __construct () {
        $this->annotationReader = new AnnotationReader();
        $this->apiParamValidation = new ApiParamValidation();
    }

    public function onKernelController (ControllerEvent $event): void {
        $this->request = $event->getRequest();
        $this->method = $event->getRequest()->getMethod();

        if (!$event->isMasterRequest()) {
            return;
        }

        $controllers = $event->getController();
        if (!is_array($controllers)) {
            return;
        }

        [$controller, $method] = $controllers;

        $this->handleApiParams($controller, $method);
    }

    /**
     * API参数校验
     * @param $controller
     * @param string $method
     * @throws \ReflectionException
     */
    private function handleApiParams ($controller, string $method): void {
        $ref = new \ReflectionClass($controller);
        $method = $ref->getMethod($method);
        $annotations = $this->annotationReader->getMethodAnnotations($method);
        $params = $this->getRequestParams();
        $errors = [];
        foreach ($annotations as $annotation) {
            if ($annotation instanceof ApiParam) {
                // require
                if ($annotation->require && !isset($params[$annotation->name])) {
                    $errors[$annotation->name][] = '字段为必填项';
                    continue;
                }
                // type
                if ($this->method == 'POST' && $annotation->type) {
                    if (isset($params[$annotation->name])) {
                        $paramType = gettype($params[$annotation->name]);
                        if ($annotation->type != $paramType) {
                            $errors[$annotation->name][] = '字段类型必须为:'.$annotation->type;
                            continue;
                        }
                    }
                }
                // validation
                if ($annotation->validation && isset($params[$annotation->name])) {
                    $validationResult = $this->apiParamValidation->do($annotation->name, $annotation->validation, $params[$annotation->name]);
                    if ($validationResult) {
                        $fieldErrors = $errors[$annotation->name] ?? [];
                        $errors[$annotation->name] = array_merge($fieldErrors, $validationResult);
                        continue;
                    }
                }
            }
        }
        if ($errors) {
            throw new ApiParamValidationException($errors);
        }
    }

    /**
     * 非GET请求时，则默认获取body中提交的json数据
     * @return array|mixed
     */
    private function getRequestParams () {
        $result = [];

        if ($this->method === 'GET') {
            $result = $this->request->query->all();
        } else {
            $result = json_decode($this->request->getContent(), true);
        }
        if (!is_array($result)) {
            $result = [];
        }

        return $result;
    }
}